/**
 * <p>A C implementation of TEA encryption algorithm.</p>
 * @author Michael
 * @version 1.0
 */

#include <stdio.h>
#include <stdint.h>
#include <string.h>

void encrypt (uint32_t* const v, const char* k) {
    uint32_t v0=v[0], v1=v[1], sum=0, s;
    uint32_t delta=0x9e3779b9;
    uint32_t key[4];
    int i;
    for (i=0; i<16; i=i+4)
	{
		key[i/4] = *(k+i)<<24|*(k+i+1)<<16|*(k+i+2)<<8|*(k+i+3);
    }
    for (s=0; s < 32; s++)
    {
        sum += delta;
        v0 += ((v1<<4) + key[0]) ^ (v1 + sum) ^ ((v1>>5) + key[1]);
        v1 += ((v0<<4) + key[2]) ^ (v0 + sum) ^ ((v0>>5) + key[3]);
    }
    v[0]=v0; v[1]=v1;
}
 
void decrypt (uint32_t* const v, const char* k) {
    uint32_t v0=v[0], v1=v[1], s;
    uint32_t delta=0x9e3779b9;
    uint32_t sum = delta<<5;
    uint32_t key[4];
    int i;
    for (i=0; i<16; i=i+4)
	{
		key[i/4] = *(k+i)<<24|*(k+i+1)<<16|*(k+i+2)<<8|*(k+i+3);
    }
    for (s=0; s<32; s++)
	{
        v1 -= ((v0<<4) + key[2]) ^ (v0 + sum) ^ ((v0>>5) + key[3]);
        v0 -= ((v1<<4) + key[0]) ^ (v1 + sum) ^ ((v1>>5) + key[1]);
        sum -= delta;
    }
    v[0]=v0; v[1]=v1;
}

void char_to_uint32(char* const c, uint32_t* a, uint32_t* b)
{
	uint32_t j[4];
	j[0] = 0xFF000000;
	j[1] = 0x00FF0000;
	j[2] = 0x0000FF00;
	j[3] = 0x000000FF;
	 
	*a = *c<<24&j[0]|*(c+1)<<16&j[1]|*(c+2)<<8&j[2]|*(c+3)&j[3];
	*b = *(c+4)<<24&j[0]|*(c+5)<<16&j[1]|*(c+6)<<8&j[2]|*(c+7)&j[3];
}

void uint32_to_char(char* const c, uint32_t a, uint32_t b)
{
	uint32_t j = 0xFF;
	*c = a>>24&j;
	*(c+1) = a>>16&j;
	*(c+2) = a>>8&j;
	*(c+3) = a&j;
	*(c+4) = b>>24&j;
	*(c+5) = b>>16&j;
	*(c+6) = b>>8&j;
	*(c+7) = b&j;
}

void encrypt_file(const char* src_file, const char* dest_file, const char* const k)
{
	if (0 == strlen(src_file) || 0 == strlen(dest_file) || strlen(k) < 16)
	{
		return;
	}
	FILE *fp = fopen(src_file, "rb");
	FILE *fpw = fopen(dest_file, "wb");
	if (NULL == fp)
	{
		return;
	}
	char buf[8];
	while(!feof(fp))
	{
		memset(buf, 0, sizeof(buf));
		size_t len = fread(buf, sizeof(char), sizeof(buf), fp);
		if (len == sizeof(buf))
		{
			uint32_t v[2];
			char_to_uint32(buf, v[0], v[1]);
			encrypt(v, k);
			char c[8];
			uint32_to_char(c, v[0], v[1]);
			fwrite(c, sizeof(char), sizeof(c), fpw);
		}
		else
		{
			fwrite(buf, sizeof(char), len, fpw);
		}
	}
	
	fclose(fp);
	fclose(fpw);
}

void decrypt_file(const char* src_file, const char* dest_file, const char* const k)
{
	if (0 == strlen(src_file) || 0 == strlen(dest_file) || strlen(k) < 16)
	{
		return;
	}
	FILE *fp = fopen(src_file, "rb");
	FILE *fpw = fopen(dest_file, "wb");
	if (NULL == fp)
	{
		return;
	}
	char buf[8];
	while(!feof(fp))
	{
		memset(buf, 0, sizeof(buf));
		size_t len = fread(buf, sizeof(char), sizeof(buf), fp);
		if (len == sizeof(buf))
		{
			uint32_t v[2];
			char_to_uint32(buf, v[0], v[1]);
			decrypt(v, k);
			char c[8];
			uint32_to_char(c, v[0], v[1]);
			fwrite(c, sizeof(char), sizeof(c), fpw);
		}
		else
		{
			fwrite(buf, sizeof(char), len, fpw);
		}	
	}
	
	fclose(fp);
	fclose(fpw);
}

int main() {
	const char* k = "abcdefghijklmnop";
	const char* src_file = "test.jpg";
	const char* dest_file = "teste.jpg";
	encrypt_file(src_file, dest_file, k);
    return 0;
}
